import dbus, dbus.mainloop.glib
from dbus import DBusException
import gobject
import avahi
import logging
    
class AvahiService:
    def __init__(self, sName, sType, sPort, sTXT="dummy", domain="local", host="", rename_count=10, mainloop=dbus.mainloop.glib.DBusGMainLoop()):
        self.serviceName = sName
        self.serviceType = sType
        self.servicePort = sPort
        self.serviceTXT = sTXT
        self.domain = domain 
        self.host = host 
        self.bus = None
        self.server = None
        self.rc = rename_count
        self.loop = mainloop
        
    def start(self):
        dbus.set_default_main_loop(self.loop)

        self.bus = dbus.SystemBus()
        self.server = dbus.Interface( 
            self.bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ), 
            avahi.DBUS_INTERFACE_SERVER )

        self.server.connect_to_signal( 'StateChanged' , 
            self.__server_state_changed)

        # Actually start
        self.__server_state_changed( self.server.GetState() )

    def __add_service(self):
        if getattr(self,'group', 2) == 2:
            self.group = dbus.Interface( 
                self.bus.get_object( avahi.DBUS_NAME, self.server.EntryGroupNew() ),
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
            self.group.connect_to_signal('StateChanged', 
                self.__entry_group_state_changed)

        # Fraught with peril
        self.group.AddService(
            avahi.IF_UNSPEC,                  # i
            avahi.PROTO_UNSPEC,               # i
            dbus.UInt32(0),                   # u
            self.serviceName,                 # s
            self.serviceType,                 # s
            self.domain,                      # s
            self.host,                        # s
            dbus.UInt16(self.servicePort),    # q
            avahi.string_array_to_txt_array(self.serviceTXT)
            )
        self.group.Commit()


    def __remove_service(self):
        if getattr(self,'group', False) and self.group:
            self.group.Reset()

    def __server_state_changed(self,state):
        if state == avahi.SERVER_COLLISION:
            logging.error(" Server name collision ")
            self.__remove_service()
        else:
            self.__add_service()

    def __entry_group_state_changed(self, state, error):
        if state == avahi.ENTRY_GROUP_COLLISION:
            logging.warning("Avahi service name collision, trying to fix")
            rc -= 1 
            if rename_count > 0:
                name = self.server.GetAlternateServiceName(name)
                self.__remove_service()
                self.__add_service()
            else:
                logging.critical("No suitable service name found")
                raise Exception("Can't start avahi service")

        elif state == avahi.ENTRY_GROUP_FAILURE:
            logging.critical("Error in group state: %s" % error)
            raise Exception("Poorly understood dbus group failure")



